package com.leyou.pojo.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;


import com.leyou.common.exceptions.LyException;
import com.leyou.pojo.dto.Payload;
import com.leyou.pojo.dto.UserDetail;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;

import javax.crypto.SecretKey;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author 虎哥
 */
public class JwtUtils {

    /**
     * JWT解析器
     */
    private final JwtParser jwtParser;
    /**
     * 秘钥
     */
    private final SecretKey secretKey;

    private final static ObjectMapper mapper = new ObjectMapper();

    private static final String KEY_PREFIX ="auth:uid:";

    @Autowired
    private StringRedisTemplate redisTemplate;

    public JwtUtils(String key) {
        // 生成秘钥
        secretKey = Keys.hmacShaKeyFor(key.getBytes(Charset.forName("UTF-8")));
        // JWT解析器
        this.jwtParser = Jwts.parserBuilder().setSigningKey(secretKey).build();
    }

    /**
     * 生成jwt，用默认的JTI
     *
     * @param userDetail 用户信息
     * @return JWT
     */
    public String createJwt(UserDetail userDetail) {
        return createJwtAndWriteRedis(userDetail, 1800);
    }

    /**
     * 生成jwt，自己指定的过期时间
     *
     * @param userDetail 用户信息
     * @return JWT
     */
//    public String createJwt(UserDetail userDetail, int expireSeconds) {
//        try {
//            // 生成token
//            String token = Jwts.builder().signWith(secretKey)
//                    .setId(createJti())
//                    .claim("user", mapper.writeValueAsString(userDetail))
//                    .setExpiration(DateTime.now().plusSeconds(expireSeconds).toDate())
//                    .compact();
//
//            return token;
//        } catch (JsonProcessingException e) {
//            throw new RuntimeException(e);
//        }
//    }

    /**
     * 创造jwt并写入redis
     * @param userDetail
     * @param
     * @return
     */
    public String createJwtAndWriteRedis(UserDetail userDetail,int expireSeconds) {
        try {
            String jti =createJti();
            // 生成token
            String jwt = Jwts.builder().signWith(secretKey)
                    .setId(jti)
                    .claim("user", mapper.writeValueAsString(userDetail))
//                    .setExpiration(DateTime.now().plusSeconds(expireSeconds).toDate())  有redis控制登录状态，那么token就不用设置失效时间
                    //因为一旦redis时效时间刷新，那么如果token到期，则会出现问题
                    .compact();
            //写入redis,设置失效时间为30分钟
            redisTemplate.opsForValue().set(KEY_PREFIX+userDetail.getId(),jti ,expireSeconds, TimeUnit.SECONDS);
            return jwt;
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 验证并解析jwt，返回包含用户信息的载荷
     *
     * @param jwt   token
     * @return 载荷，包含JTI和用户信息
     */
//    public Payload parseJwt(String jwt) {
//        try {
//            Jws<Claims> claimsJws = jwtParser.parseClaimsJws(jwt);
//            Claims claims = claimsJws.getBody();
//
//            Payload payload = new Payload();
//            payload.setJti(claims.getId());
//            payload.setUserDetail(mapper.readValue(claims.get("user", String.class), UserDetail.class));
//            return payload;
//        }catch (IllegalArgumentException e) {
//            throw new LyException("用户为登录",401);
//        }catch (JwtException e) {
//            throw new LyException("登录无效或者已经超时", 401);
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }
//    }


    /**
     * 验证并解析jwt，返回包含用户信息的载荷
     *
     * @param jwt   token
     * @return 载荷，包含JTI和用户信息
     */
    public Payload parseJwtAndRedis(String jwt) {
        try {
            Jws<Claims> claimsJws = jwtParser.parseClaimsJws(jwt);
            Claims claims = claimsJws.getBody();

            Payload payload = new Payload();
            payload.setJti(claims.getId());
            payload.setUserDetail(mapper.readValue(claims.get("user", String.class), UserDetail.class));
            String s = redisTemplate.opsForValue().get(KEY_PREFIX + payload.getUserDetail().getId());
            if(!StringUtils.equals(s,payload.getJti())){
                throw new LyException("你的账号已在别处登录或被管理员禁止登录",401);
            }
            return payload;
        }catch (IllegalArgumentException e) {
            throw new LyException("用户为登录",401);
        }catch (JwtException e) {
            throw new LyException("登录无效或者已经超时", 401);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**\
     * 刷新redis
     * @param
     * @return
     */
    public void refreshRedis(String uId){
        redisTemplate.expire(KEY_PREFIX+uId,18000, TimeUnit.SECONDS);
    }
    public void refreshRedis(String uId,Long timeUnitSeconds){
        redisTemplate.expire(KEY_PREFIX+uId,timeUnitSeconds, TimeUnit.SECONDS);
    }
    private String createJti() {
        return StringUtils.replace(UUID.randomUUID().toString(), "-", "");
    }


    public void deleteJwt(String uid){
        redisTemplate.delete(KEY_PREFIX+uid);
    }
}